import { ElMessage, UploadRawFile } from 'element-plus'
import { IChunkInfo, IFileRecord, IFileUrl } from '@/types'
import createService from '@/utils/axiosUtil.ts'
import { getFileMD5 } from '@/utils/cryptoUtil.ts'
import Compressor from 'compressorjs'

/**
 * @deprecated 获取assets静态资源
 * @param url 路径
 */
export const getAssetsImageFile = (url: string) => {
  return new URL(url, import.meta.url).href
}

/**
 * 图片转Base64
 * @param file 图片文件
 */
export const parseImageToBase64 = (file: File) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = () => {
      // 图片转base64完成后返回reader对象
      resolve(reader.result)
    }
    reader.onerror = reject
  })
}

/**
 * 加载JSON文件
 * @param filePath 文件路径
 */
export const loadJsonFile = (filePath: string) => {
  return new Promise((resolve, reject) => {
    fetch(filePath)
      .then((response) => {
        if (!response.ok) {
          reject(new Error(`无法加载文件: ${filePath}`))
        }
        return response.json()
      })
      .then((data) => resolve(data))
      .catch((error) => reject(error))
  })
}

/**
 * 上传文件API
 * @param url url
 * @param form 表单
 * @param path 文件路径
 * @param md5 文件md5
 */
export const uploadFileApi = (url: string, form: FormData,
  path: string, md5: string): Promise<any> => {
  const fileService = createService()
  return fileService({
    url,
    timeout: 10000,
    headers: {
      'Content-Type': 'multipart/form-data'
    },
    method: 'post',
    params: { path, md5 },
    data: form
  })
}

/**
 * 上传文件块API
 * @param url url
 * @param offset 当前文件块序号
 * @param form 表单
 * @param path 文件路径
 * @param progressHandler 上传进度处理器
 */
export const uploadChunkFileApi = (url: string, offset: number, form: FormData,
  path: string, progressHandler: (event: ProgressEvent, offset: number) => void): Promise<any> => {
  const fileService = createService()
  return fileService({
    url,
    timeout: 10000,
    method: 'post',
    params: { path },
    data: form,
    onUploadProgress: (progressEvent: ProgressEvent) => {
      progressHandler(progressEvent, offset)
    }
  })
}

/**
 * 合并文件
 * @param url url
 * @param filename 文件名
 * @param path 路径
 */
export const mergeFileAip = (url: string, filename: string, path: string): Promise<IFileUrl> => {
  const fileService = createService()
  return fileService({
    url,
    method: 'post',
    params: { filename, path }
  })
}

/**
 * 校验上传的文件
 * @param file 文件
 * @param fileTypeList 文件类型列表
 * @param fileTypeName 文件类型名称
 * @param fileSize 文件大小 单位：M
 */
export const checkUploadFile = (file: UploadRawFile,
  fileTypeList: string[], fileTypeName: string, fileSize = 10): boolean => {
  const maxFileMax = 1024 * 1024 * fileSize
  if (!fileTypeList.includes(file.type)) {
    ElMessage.error(`请上传${fileTypeName}格式!`)
    return false
  }

  if (file.size > maxFileMax) {
    ElMessage.error(`请上传${fileSize}M以内的文件!`)
    return false
  }

  return true
}

/**
 * 上传文件
 * @param url 路径
 * @param file 文件
 * @param path 路径
 * @param isComPress 是否压缩
 */
export const doUploadFile = async (url: string, file: File,
  path: string, isComPress: boolean): Promise<IFileUrl> => {
  const compressOptions = {
    quality: 0.7,
    maxHeight: 800,
    maxWidth: 600
  }

  const compressedFile = isComPress ? await compressImage(file, compressOptions) : file

  const formData = new FormData()
  formData.append('file', compressedFile)

  const md5 = await getFileMd5(compressedFile)
  // 1. 上传文件
  const result: IFileRecord = await uploadFileApi(url, formData, path, md5)
  // 2. 返回图片信息
  return {
    id: result.id,
    name: result.name,
    url: result.url
  }
}

/**
 * 上传文件块
 * @param url 路径
 * @param offset 当前文件块序号
 * @param chunkInfo 文件块信息
 * @param chunkFile 文件块
 * @param path 路径
 * @param progressHandler 上传进度处理器
 */
export const doUploadChunk = async (url: string,
  offset: number, chunkInfo: IChunkInfo, chunkFile: Blob, path: string,
  progressHandler: (event: ProgressEvent, offset: number) => void) => {
  const formData = new FormData()
  Object.entries(chunkInfo).forEach(([k, v]) => {
    formData.append(k, v)
  })
  // 文件块参数 需要和属性名对应！
  formData.append('multipartFile', chunkFile, chunkInfo.filename)

  await uploadChunkFileApi(url, offset, formData, path, progressHandler)
}

/**
 * 计算文件md5值
 */
export const getFileMd5 = (file: File): Promise<string> => {
  return new Promise((resolve) => {
    const fileReader = new FileReader()
    fileReader.readAsArrayBuffer(file)

    fileReader.onload = (event) => {
      const fileMd5 = getFileMD5(event.target?.result as ArrayBuffer)
      resolve(fileMd5)
    }
  })
}

/**
 * 获取文件名（不含后缀）
 * @param file 文件
 */
export const getFileName = (file: File) => {
  return file.name.replace(/(.*\/)*([^.]+).*/ig, '$2')
}

/**
 * 把字节转换成正常文件大小
 * @param size 字节
 */
export const formatByte = (size: number) => {
  if (!size) return ''
  const num = 1024.00 // byte
  if (size < num) return `${size}B`
  if (size < Math.pow(num, 2)) return `${(size / num).toFixed(2)}KB` // kb
  if (size < Math.pow(num, 3)) return `${(size / Math.pow(num, 2)).toFixed(2)}MB` // M
  if (size < Math.pow(num, 4)) return `${(size / Math.pow(num, 3)).toFixed(2)}G` // G
  return `${(size / Math.pow(num, 4)).toFixed(2)}T` // T
}


/**
 * 格式化时间
 * @param seconds 秒数
 */
export const formatTimes = (seconds: number) => {
  let hour: string | number = parseInt(String(((seconds / 60) / 60) % 24), 10)
  let minute: string | number = parseInt(String((seconds / 60) % 60), 10)
  let second: string | number = parseInt(String(seconds % 60), 10)
  // 如果小于10 则在前边进行补零
  hour = hour < 10 ? `0${hour}` : hour
  minute = minute < 10 ? `0${minute}` : minute
  second = second < 10 ? `0${second}` : second
  return `${hour}:${minute}:${second}`
}

/**
 * 压缩图片文件
 * @param file 原始文件对象
 * @param options 压缩选项
 * @returns Promise<File> 返回压缩后的文件
 */
export const compressImage = async (
  file: File,
  options: {
      quality: number;
      maxWidth: number;
      maxHeight: number;
    }
): Promise<File> => {
  return new Promise((resolve, reject) => {
    new Compressor(file, {
      quality: options.quality,
      maxWidth: options.maxWidth,
      maxHeight: options.maxHeight,
      resize: 'cover',
      success(result) {
        const compressedFile = new File([result], file.name, {
          type: result.type,
          lastModified: Date.now()
        })
        resolve(compressedFile)
      },
      error(err) {
        reject(err)
      }
    })
  })
}
